#include "HelloTriangleApplication.h"

#define STB_IMAGE_IMPLEMENTATION // 不定义这个宏值， stb库仅仅是头文件,无法链接函数
#include "stb_image.h"


#ifdef SPV_FILEDIR
#define VERT_SPV_FILE SPV_FILEDIR "\\vert.spv"
#define FRAG_SPV_FILE SPV_FILEDIR "\\frag.spv"
#else
#define VERT_SPV_FILE ".\\vert.spv"
#define FRAG_SPV_FILE ".\\frag.spv"
#endif

const uint32_t WIDTH = 800;
const uint32_t HEIGHT = 600;
const int MAX_FRAMES_IN_FLIGHT = 2;  // 同时处理渲染多少帧

const std::vector<const char*> validationLayers = {
    "VK_LAYER_KHRONOS_validation"  //  LunarG的Vulkan SDK 提供的校验层 层名
};

const std::vector<const char*> deviceExtensions = {
    VK_KHR_SWAPCHAIN_EXTENSION_NAME  //  swap chain 设备扩展特性名
};

#ifdef NDEBUG
const bool enableValidationLayers = true; //  先一直打开吧，设置为 false表明release版本不使用校验层
#else
const bool enableValidationLayers = true;
#endif

/**
     * @brief VK_EXT_debug_utils调试信息扩展功能的回调函数,原型为 PFN_vkDebugUtilsMessengerCallbackEXT
     * @param messageSeverity
     * @param messageType
     * @param pCallbackData
     * @param pUserData
     * @return
     */
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
        VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,  // 消息类型
        VkDebugUtilsMessageTypeFlagsEXT messageType,
        const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
        void* pUserData) {

    //messageSeverity  参考 VK_DEBUG_UTILS_MESSAGE_SEVERITY_xxxx
    //messageType  参考 VK_DEBUG_UTILS_MESSAGE_TYPE_xxxx
    //pCallbackData 返回VkDebugUtilsMessengerCallbackDataEXT结构数据，包含以下成员
    //  pMessage: 一个以null结尾的包含调试信息的字符串
    //  pObjects: 存储有和消息相关的vulkan对象句柄的数组
    //  objectCount: pObjects的数组长度
    //pUserData是一个指向了我们设置回调函数时，传递的数据的指针。

    switch (messageSeverity) {
    case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
        std::cout << "validation warning: " << pCallbackData->pMessage << std::endl;
        break;
    case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
        std::cout << "validation error: " << pCallbackData->pMessage << std::endl;
        break;
    default:
        break;
    }

//    std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;

    //回调函数返回了一个布尔值，用来表示引发校验层处理的调用是否被中断
    //如果返回值为true，对应调用就会返回VK_ERROR_VALIDATION_FAILED_EXT并终止
    //通常，只在测试校验层本身时会返回True，其余情况下，回调函数应该返回False。
    return VK_FALSE;
}

/**
 * @brief vulkan  CreateDebugUtilsMessengerEXT函数的自动加加载封装
 * @param instance
 * @param pCreateInfo
 * @param pAllocator
 * @param pDebugMessenger
 * @return
 */
VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) {
    //  由于创建调试信息对象的函数vulkan默认是不加载的，所以要手动加载函数
    auto func = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(
                vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT")
                );
    if (func != nullptr) {
        return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
    } else {
        return VK_ERROR_EXTENSION_NOT_PRESENT;
    }
}

/**
 * @brief 调试消息对象默认信息设置函数
 * @param createInfo
 */
void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
    createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;

    // 消息级别和类型选择
    createInfo.messageSeverity =
            VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
            VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
            VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;

    createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
            VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
            VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;

    createInfo.pfnUserCallback = debugCallback;

    createInfo.pUserData = nullptr;
}

VkShaderModule createShaderModule(VkDevice device, const std::vector<char>& code) {
    VkShaderModuleCreateInfo createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
    createInfo.codeSize = code.size();
    // 由于pCode 是uint32_t*指针类型， 使用reinterpret_cast转换类型应保证内存类型地址对齐，
    //  这里没有处理是由于std::vector类默认内存申请其地址对齐肯定满足uint32_t类型
    createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());

    // 创建着色器模块, 只是spir-v二进制节字码的一个轻量封装
    //  创建执行完后， createInfo.pCode指向的代码内存可以立即被释放的
    VkShaderModule shaderModule;
    if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
        throw std::runtime_error("failed to create shader module!");
    }

    return shaderModule;
}

HelloTriangleApplication::HelloTriangleApplication():
    m_window(nullptr),
    m_physicalDevice(VK_NULL_HANDLE),
    m_device(VK_NULL_HANDLE),
    m_graphicsQueue(VK_NULL_HANDLE),
    m_presentQueue(VK_NULL_HANDLE),
    m_abs_surface(VK_NULL_HANDLE),
    m_swapChain(VK_NULL_HANDLE),
    m_renderPass(VK_NULL_HANDLE),
    m_descriptorSetLayout(VK_NULL_HANDLE),
    m_descriptorPool(VK_NULL_HANDLE),
    m_pipelineLayout(VK_NULL_HANDLE),
    m_graphicsPipeline(VK_NULL_HANDLE),
    m_commandPool(VK_NULL_HANDLE),
    m_framebufferResized(false),
    m_currentFrame(0),
    m_vertexBuffer(VK_NULL_HANDLE),
    m_vertexBufferMemory(VK_NULL_HANDLE)
{
//    std::cout<<VERT_SPV_FILE<<std::endl;
//    std::cout<<FRAG_SPV_FILE<<std::endl;
}

HelloTriangleApplication::~HelloTriangleApplication()
{

}

void HelloTriangleApplication::run()
{
    initWindow();
    initVulkan();
    mainLoop();
    cleanup();
}


static void framebufferResizeCallback(GLFWwindow* window, int width, int height) {
    auto resized = reinterpret_cast<bool *>(glfwGetWindowUserPointer(window));
    *resized = true;
}

void HelloTriangleApplication::initWindow()
{
    glfwInit();  // 初始化glfw库
    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);  //  提示GLFW不要进行opengl上下文初始化(glfw默认会初始化opengl)
//    glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);  //  暂禁止窗口缩放， 减少代码复杂度
    glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

    m_window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);  // 创建窗体
    glfwSetWindowUserPointer(m_window, &(this->m_framebufferResized));
    glfwSetFramebufferSizeCallback(m_window,framebufferResizeCallback);    //  设置帧缓冲尺寸改变回调函数
}

bool HelloTriangleApplication::checkValidationLayerSupport(){
    uint32_t layerCount;
    vkEnumerateInstanceLayerProperties(&layerCount, nullptr);  //  查询支持的层数量

    std::vector<VkLayerProperties> availableLayers(layerCount);
    vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());  // 获取支持的层内容

    // 遍历所有支持的层名， 判断是否存在需要的使用的层
    for (const char* layerName : validationLayers) {
        bool layerFound = false;

        for (const auto & layerProperties : availableLayers) {
            if (strcmp(layerName, layerProperties.layerName) == 0) {
                layerFound = true;
                break;
            }
        }

        if (!layerFound) {
            return false;
        }
    }

    return true;
}

void HelloTriangleApplication::createInstance()
{

    //  查询是否支持需要使用到的层
    if (enableValidationLayers && !checkValidationLayerSupport()) {
        throw std::runtime_error("validation layers requested, but not available!");
    }

    /*
    VkApplicationInfo可用于将应用信息告诉vulkan库
    这些信息的填写不是必须的，但填写的信息可能会作为驱动程序的优化依据，让驱动程序进行一些特殊的优化。
    比如，应用程序使用了某个引擎，驱动程序对这个引擎有一些特殊处理，这时就可能有很大的优化提升：
    */
    VkApplicationInfo appInfo{};
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;  // vulkan的很多结构体需要我们显式地在sType成员变量中指定结构体的类型
    appInfo.pApplicationName = "Hello Triangle";
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.pEngineName = "No Engine";
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.apiVersion = VK_API_VERSION_1_0; //VK_API_VERSION_1_2
    appInfo.pNext = nullptr;    // pNext成员变量用来指向未来可能扩展的参数信息，现在并没有使用它，将其设置为null


    /*
    下面的这个结构体是必须的，它告vulkan的驱动程序需要使用的全局扩展和校验层。
    全局是指这里的设置对于整个应用程序都有效，而不仅仅对一个设备有效
    */

    VkInstanceCreateInfo createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    createInfo.pApplicationInfo = &appInfo;

    //    vulkan是平台无关的，所以需要一个和窗口系统交互的扩展。
    //    glfw库包含了一个可以返回这一扩展的函数这里直接使用它
    uint32_t glfwExtensionCount = 0;
    const char** glfwExtensions = nullptr;
    glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

    {
        //  查询当前vulkan运行环境支持的扩展
        uint32_t extensionCount = 0;
        vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); // 查询支持扩展的数量
        std::vector<VkExtensionProperties> extensions(extensionCount);
        vkEnumerateInstanceExtensionProperties(nullptr,     // 校验层,对扩展进行过滤，暂时设置null
                                               &extensionCount,
                                               extensions.data());  // 获取支持的扩展详细内容
        std::cout << "available extensions:\n";
        for (const auto& extension : extensions) {  //  打印支持扩展的信息
            std::cout << '\t' << extension.extensionName << '\n';
        }
    }


    //  添加除glfw窗口需求以外的扩展
    std::vector<const char*> extensions(glfwExtensions,
                                        glfwExtensions + glfwExtensionCount);

    if (enableValidationLayers) {
        extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);  //  VK_EXT_debug_utils 宏别名
    }

    // 使能需要用到的扩展
    createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
    createInfo.ppEnabledExtensionNames = extensions.data();


    //  打开需要用到的层， 目前是lunarG vulkanSDK提供的校验层
    VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};  //  用于 创建vulkan实例和销毁vulkan时的调试信息
    if (enableValidationLayers) {
        createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
        createInfo.ppEnabledLayerNames = validationLayers.data();

        // 校验层提供的作用
        //        检测参数值是否合法
        //        追踪对象的创建和清除操作，发现资源泄漏问题
        //        追踪调用来自的线程，检测是否线程安全。
        //        将API调用和调用的参数写入日志
        //        追踪API调用进行分析和回放

        //

        populateDebugMessengerCreateInfo(debugCreateInfo);

        //设置pNext为VkDebugUtilsMessengerCreateInfoEXT结构指针就可以
        //在创建vulkan实例和销毁vulkan时的显示调试信息
        createInfo.pNext = reinterpret_cast<VkDebugUtilsMessengerCreateInfoEXT*>(
                    &debugCreateInfo
                    );
    } else {
        createInfo.enabledLayerCount = 0;
        createInfo.pNext = nullptr;
    }

    // 创建vulkan实例
    VkResult result = vkCreateInstance(&createInfo,  //  vkCreatexxxx 函数一般需要一个信息
                                       nullptr,     //  vkCreatexxxx 函数一般可设置一个内存分配回调函数，目前一般设置为null
                                       &m_instance  //  返回创建的变量
                                       );
    // result，vulkan库函数一般都返回执行结果
    if (result != VK_SUCCESS) {
        throw std::runtime_error("failed to create instance!");
    }

}

void HelloTriangleApplication::setupDebugMessenger()
{
    if (!enableValidationLayers) return;

    VkDebugUtilsMessengerCreateInfoEXT createInfo{};

//    createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;

//    // 消息级别和类型选择
//    createInfo.messageSeverity =
//            VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
//            VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
//            VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
//    createInfo.messageType =
//            VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
//            VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
//            VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;

//    createInfo.pfnUserCallback = debugCallback;
//    createInfo.pUserData = nullptr; // Optional
    populateDebugMessengerCreateInfo(createInfo);




    // 在vulkan实例中创建调试消息对象
    auto ret = CreateDebugUtilsMessengerEXT(m_instance,&createInfo,nullptr,&m_debugMessenger);
    if(ret != VK_SUCCESS){
        throw std::runtime_error("failed to set up debug messenger!");
    }
}

void HelloTriangleApplication::createSurface()
{
#ifdef ManualCreateSurface
    //  这里只是vulkan 表面创建示例用的， 平台无关glfw glfwCreateWindowSurface函数可实现这些功能
    VkWin32SurfaceCreateInfoKHR createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
    createInfo.hwnd = glfwGetWin32Window(m_window);  //  获取窗体句柄
    createInfo.hinstance = GetModuleHandle(nullptr);    // 获取程序进程句柄

    //  使用台平相关的函数创建渲染表面
    //  常用的 vkCreateWin32SurfaceKHR : windows平台, vkCreateXcbSurfaceKHR : linux平台, 这两个函数太常见了不用调用vkGetInstanceProcAddr来加载
    //  其它的平台可能要使用vkGetInstanceProcAddr来加载对应的函数
    if (vkCreateWin32SurfaceKHR(m_instance, &createInfo, nullptr, &m_abs_surface) != VK_SUCCESS) {
        throw std::runtime_error("failed to create window surface!");
    }
#else
    //  glfw自带的glfwCreateWindowSurface实现自动创建平台无关的渲染表面
    if (glfwCreateWindowSurface(m_instance, m_window, nullptr, &m_abs_surface) != VK_SUCCESS) {
        throw std::runtime_error("failed to create window surface!");
    }
#endif
}

HelloTriangleApplication::SwapChainSupportDetails HelloTriangleApplication::querySwapChainSupport(VkPhysicalDevice device)
{
    SwapChainSupportDetails details;
    vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, m_abs_surface, &details.capabilities);

    uint32_t formatCount;
    vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_abs_surface, &formatCount, nullptr);

    if (formatCount != 0) {
        details.formats.resize(formatCount);
        vkGetPhysicalDeviceSurfaceFormatsKHR(device, m_abs_surface, &formatCount, details.formats.data());
    }

    uint32_t presentModeCount;
    vkGetPhysicalDeviceSurfacePresentModesKHR(device, m_abs_surface, &presentModeCount, nullptr);

    if (presentModeCount != 0) {
        details.presentModes.resize(presentModeCount);
        vkGetPhysicalDeviceSurfacePresentModesKHR(device, m_abs_surface, &presentModeCount, details.presentModes.data());
    }

    return details;
}

void HelloTriangleApplication::pickPhysicalDevice()
{
    uint32_t deviceCount = 0;
    // 先查询可用的设备数量
    vkEnumeratePhysicalDevices(m_instance, &deviceCount, nullptr);
    if (deviceCount == 0) {
        throw std::runtime_error("failed to find GPUs with Vulkan support!");
    }

    //  再获取所示设备句柄
    std::vector<VkPhysicalDevice> devices(deviceCount);
    vkEnumeratePhysicalDevices(m_instance, &deviceCount, devices.data());

    { //遍历检查符合需要的显卡设备
//        auto isDeviceSuitable = [](VkPhysicalDevice device) -> bool{
//            // 获取属性,例如名称，类型和支持的vulkan版本
//            VkPhysicalDeviceProperties deviceProperties;
//            vkGetPhysicalDeviceProperties(device, &deviceProperties);

//            // 获取特性，例如64位浮点、纹理压缩、多视口等
//            VkPhysicalDeviceFeatures deviceFeatures;
//            vkGetPhysicalDeviceFeatures(device, &deviceFeatures);

//            bool ret = true;

//            // 示例，判断独立显卡且有几何着色器特性
//            ret = deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
//                    && deviceFeatures.geometryShader;

//            return ret;
//        }; //  判断函数isDeviceSuitable

        auto rateDeviceSuitability = [](VkPhysicalDevice device) -> int{
            // 获取属性,例如名称，类型和支持的vulkan版本
            VkPhysicalDeviceProperties deviceProperties;
            vkGetPhysicalDeviceProperties(device, &deviceProperties);

            // 获取特性，例如64位浮点、纹理压缩、多视口等
            VkPhysicalDeviceFeatures deviceFeatures;
            vkGetPhysicalDeviceFeatures(device, &deviceFeatures);

            int score = 0;

            // Discrete GPUs have a significant performance advantage
            if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
                score += 1000;
            }

            // Maximum possible size of textures affects graphics quality
            score += deviceProperties.limits.maxImageDimension2D;

            // Application can't function without geometry shaders
            if (!deviceFeatures.geometryShader) {
                return 0;
            }

            return score;
        }; //  打分函数 rateDeviceSuitability

        std::multimap<int, VkPhysicalDevice> candidates;  //  候选显卡与其分数

//        for (const auto& device : devices) {
//            if (isDeviceSuitable(device)) {
//                m_physicalDevice = device;
//                break;
//            }
//        }


        for (const auto& device : devices) {
            int score = rateDeviceSuitability(device);
            candidates.insert(std::make_pair(score, device));
        }

        // Check if the best candidate is suitable at all
        // rbegin返回指向容器中最后一个（已排好序的最后一个）元素的反向双向迭代器。
        if (candidates.rbegin()->first > 0) {
            m_physicalDevice = candidates.rbegin()->second;
        }

        //  查询物理设备是否有需要的扩展特性
        uint32_t extensionCount;
        vkEnumerateDeviceExtensionProperties(m_physicalDevice, nullptr, &extensionCount, nullptr);

        std::vector<VkExtensionProperties> availableExtensions(extensionCount);
        vkEnumerateDeviceExtensionProperties(m_physicalDevice, nullptr, &extensionCount, availableExtensions.data());

        std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());

        for (const auto& extension : availableExtensions) {
            requiredExtensions.erase(extension.extensionName);
        }
        VkPhysicalDeviceProperties deviceProperties;
        vkGetPhysicalDeviceProperties(m_physicalDevice, &deviceProperties);
        if(!requiredExtensions.empty()){
            std::cerr << "Require extension properties is not sufficient on device " << deviceProperties.deviceName << std::endl;
            throw std::runtime_error("Require extension properties is not satisfied!");
        }

        //  查询物理设备的swap chain一些明细信息
        bool swapChainAdequate = false;
        SwapChainSupportDetails swapChainSupport = querySwapChainSupport(m_physicalDevice);
        swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
        if(!swapChainAdequate){
            throw std::runtime_error("Can not get swap chain detail info on device!");
        }

        //  查询和获取选择出来的设备的驱动所包含的vulkan驱动队列簇， 判断是否有需要的队列簇
        //  一般的图形应用程序只用一个有处理graphics命令的队列就能满足要求了(实际上显卡一般只有一个graphics队列簇)
        //  复杂一点的图形应用程序有一个专门处理graphics+compute命令的队列、一个专门处理compute命令的队列用于异步计算、一个专门进行异步传输的队列。
        //  graphics和compute队列就算没有标示能进行传输命令，它也一定能执行传输命令的
        //  只能transfer的队列簇一般用于cpu和gpu之间数据传输, gpu内部数据传输使用graphics或compute队列簇效果要高点
        if(m_physicalDevice != VK_NULL_HANDLE){
            uint32_t queueFamilyCount = 0;
            vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, nullptr);

            std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
            vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, queueFamilies.data());

            //            VK_QUEUE_GRAPHICS_BIT 支持图形操作
            //            VK_QUEUE_COMPUTE_BIT 支持计算操作
            //            VK_QUEUE_TRANSFER_BIT 支持传输操作
            VkQueueFlags flags_filter = VK_QUEUE_GRAPHICS_BIT;  //  必须支持的驱动队列类型
            VkQueueFlags flags_opt = 0;   //  可选支持的驱动队列类型
            VkQueueFlags flags_max = 0;
            uint32_t i = 0;
            for(const auto & q : queueFamilies){
                auto t = q.queueFlags & flags_filter;
                if(t){
                    t = t | (q.queueFlags & flags_opt);
                    if(t > flags_max){
                        if(q.queueFlags & VK_QUEUE_GRAPHICS_BIT){
                            m_devQueFamilies[QueFamiliesKey::Graphics] = static_cast<uint32_t>(i);
                        }
                        flags_max = t;
                    }
                }
                i++;
            }

            VkBool32 presentSupport = false;
            auto it = m_devQueFamilies.find(QueFamiliesKey::Graphics);
            if(it != m_devQueFamilies.end()){
                vkGetPhysicalDeviceSurfaceSupportKHR(m_physicalDevice, it->second, m_abs_surface, &presentSupport);
                if(presentSupport){
                    m_devQueFamilies[QueFamiliesKey::SurfaceSupportKHR] = it->second;
                }
            }

            it = m_devQueFamilies.find(QueFamiliesKey::SurfaceSupportKHR);
            if( it == m_devQueFamilies.end()){
                i = 0;
                for(const auto & q : queueFamilies){
                    presentSupport = false;
                    vkGetPhysicalDeviceSurfaceSupportKHR(m_physicalDevice, i, m_abs_surface, &presentSupport);
                    if(presentSupport){
                        m_devQueFamilies[QueFamiliesKey::SurfaceSupportKHR] = i;
                        break;
                    }
                    i++;
                }
            }

//            VkPhysicalDeviceProperties deviceProperties;
//            vkGetPhysicalDeviceProperties(m_physicalDevice, &deviceProperties);
            if(m_devQueFamilies.size() < 1){
                m_physicalDevice = VK_NULL_HANDLE;
                std::cerr << "No usable queues in device " << deviceProperties.deviceName << std::endl;
            }else{
                std::cout << "Usable queues in device " << deviceProperties.deviceName << " : " << m_devQueFamilies.size() << std::endl;
            }
        }

        if (m_physicalDevice == VK_NULL_HANDLE) {
            throw std::runtime_error("failed to find a suitable GPU!");
        }
    }
}

void HelloTriangleApplication::createLogicalDevice()
{
    if(m_physicalDevice == VK_NULL_HANDLE) return;

    std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
    float queuePriority[] = {1.0f};  // 告诉驱动.队列的优先级，0~1.0
    std::set<uint32_t> uniqueQueueFamilies = {m_devQueFamilies[QueFamiliesKey::Graphics],
                                              m_devQueFamilies[QueFamiliesKey::SurfaceSupportKHR]};
    for(uint32_t queueFamily : uniqueQueueFamilies){
        VkDeviceQueueCreateInfo queueCreateInfo{};  //  声明要创建的队列所属的队列家族，和要创建的队列数量，一般只创建一个就可以满足多线程调用了
        queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
        queueCreateInfo.queueFamilyIndex = queueFamily;
        queueCreateInfo.queueCount = 1;
        queueCreateInfo.pQueuePriorities = queuePriority;
        queueCreateInfos.push_back(queueCreateInfo);
    }

    //  声明需要用到的设备特性（列如几何着色器等）， 这里暂时全部false,以后扩展
    VkPhysicalDeviceFeatures deviceFeatures{};
    deviceFeatures.samplerAnisotropy = VK_TRUE; //  采样器各向异性过滤附加设备特性使能

    //  准备好创建逻辑设备信息结构
    VkDeviceCreateInfo createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
    createInfo.pQueueCreateInfos =  queueCreateInfos.data();
    createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
    createInfo.pEnabledFeatures = &deviceFeatures;

    createInfo.ppEnabledExtensionNames = deviceExtensions.data();   //  使能要求的设备扩展特性
    createInfo.enabledExtensionCount =static_cast<uint32_t>(deviceExtensions.size());

    if (enableValidationLayers) {
        //  扩展层在vulkan 1.0版本后与创建vulkan实例不存在区别了，这里只是为了兼容而设置这些层
        createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
        createInfo.ppEnabledLayerNames = validationLayers.data();
    } else {
        createInfo.enabledLayerCount = 0;
    }

    //  创建逻辑设备实例
    if (vkCreateDevice(m_physicalDevice, &createInfo, nullptr, &m_device) != VK_SUCCESS) {
        throw std::runtime_error("failed to create logical device!");
    }

    //  从指定的逻辑设备、指定的队列家族获取第n个驱动队列，目前只创建了一个队列所以指定索引0队列
    //  每个队列同一时间只能有接受单个线程命令提交， 但不同的线程可以同时发送命令到不同的队列
    vkGetDeviceQueue(m_device, m_devQueFamilies[QueFamiliesKey::Graphics], 0, &m_graphicsQueue);
    vkGetDeviceQueue(m_device, m_devQueFamilies[QueFamiliesKey::SurfaceSupportKHR], 0, &m_presentQueue);

}

void HelloTriangleApplication::createSwapChain()
{
    SwapChainSupportDetails swapChainSupport = querySwapChainSupport(m_physicalDevice);

    VkSurfaceFormatKHR surfaceFormat = swapChainSupport.formats[0];  //  选择可用的显示图像格式， 一般使用BGRA 8位 SRGB图
    for (const auto& availableFormat : swapChainSupport.formats) {
        if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
            surfaceFormat = availableFormat;
        }
    }

    VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;   //  选择图像的刷新模式
    for (const auto& availablePresentMode : swapChainSupport.presentModes) {
        if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
            presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
        }
    }

    VkExtent2D extent;      //  获取图像大小
    if (swapChainSupport.capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {  // 像素与显示屏单位是1:1的情况
        extent = swapChainSupport.capabilities.currentExtent;
    } else {  // 像素与显示屏单位不是1:1的情况
        int width, height;
        glfwGetFramebufferSize(m_window, &width, &height);

        VkExtent2D actualExtent = {
            static_cast<uint32_t>(width),
            static_cast<uint32_t>(height)
        };

        const auto & minE = swapChainSupport.capabilities.minImageExtent;
        const auto & maxE = swapChainSupport.capabilities.maxImageExtent;
        const auto & w = actualExtent.width;
        const auto & h = actualExtent.height;
        actualExtent.width = (w >= minE.width && w<= maxE.width) * w
                + (w < minE.width) * minE.width
                + (w > maxE.width) * maxE.width
                ;
        actualExtent.height = (h >= minE.height && h<= maxE.height) * h
                + (h < minE.height) * minE.height
                + (h > maxE.height) * maxE.height
                ;
        extent = actualExtent;
    }

    uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;  // swap chain使用的缓冲图像数量, 这里设置成最小值加1
    if (swapChainSupport.capabilities.maxImageCount > 0   //  最大值为0表示没有最大值限制
            && imageCount > swapChainSupport.capabilities.maxImageCount) {  //  确保没有超过最大值
        imageCount = swapChainSupport.capabilities.maxImageCount;
    }

    VkSwapchainCreateInfoKHR createInfo{};  // swap chain 创建信息结构
    createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
    createInfo.surface = m_abs_surface;
    createInfo.minImageCount = imageCount;
    createInfo.imageFormat = surfaceFormat.format;
    createInfo.imageColorSpace = surfaceFormat.colorSpace;
    createInfo.imageExtent = extent;
    createInfo.imageArrayLayers = 1;  //  图像层数组长度， 一般为1， 除非渲染例如3D眼镜设备
    //  改用 VK_IMAGE_USAGE_TRANSFER_DST_BIT 之类的值，可使用内存操作将渲染图像传输到交换链图像
    createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;  //  使用swap chain进行何种操作， 这里只进行颜色附件操作;

    uint32_t queueFamilyIndices[] = {m_devQueFamilies[QueFamiliesKey::Graphics],
                                     m_devQueFamilies[QueFamiliesKey::SurfaceSupportKHR]};
    if(m_devQueFamilies[QueFamiliesKey::Graphics] != m_devQueFamilies[QueFamiliesKey::SurfaceSupportKHR]){
        //  绘图和显示驱动队列不是同一个队列
        // VK_SHARING_MODE_CONCURRENT 图像可以在多个队列族中使用，无需显式所有权转移
        createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
        createInfo.queueFamilyIndexCount = 2;   //  指定在并发模式时，那些队列共享图像的所有权
        createInfo.pQueueFamilyIndices = queueFamilyIndices;
    }else{
        //  绘图和显示驱动队列相同
        // VK_SHARING_MODE_EXCLUSIVE 图像一次由一个队列家族拥有，所有权必须明确转移，然后才能在另一个队列家族中使用。此选项提供最佳性能
        createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
        createInfo.queueFamilyIndexCount = 0; // Optional
        createInfo.pQueueFamilyIndices = nullptr; // Optional
    }

    //  设置前置的几何变换（例如可将显示旋转90度等）， 这里设置当前几何变换表示进行前置变换
    createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
    //  设置图像像素透明度的混合模式， 这里设置为 忽略 透明度
    createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;

    createInfo.presentMode = presentMode;
    createInfo.clipped = VK_TRUE;  //  是否允许像素被剪裁（例如窗体被遮挡）； 当要读回显示的像素值时设置为false

    createInfo.oldSwapchain = VK_NULL_HANDLE;   //  旧的交换链， 一般用于无缝进行交换链重新创建

    //  创建swap chain
    if (vkCreateSwapchainKHR(m_device, &createInfo, nullptr, &m_swapChain) != VK_SUCCESS) {
        throw std::runtime_error("failed to create swap chain!");
    }

    //  从swap chain中获取缓冲图像数组
    vkGetSwapchainImagesKHR(m_device, m_swapChain, &imageCount, nullptr);  //  按实际缓冲图像设置数组
    m_swapChainImages.resize(imageCount);
    vkGetSwapchainImagesKHR(m_device, m_swapChain, &imageCount, m_swapChainImages.data());
    m_swapChainImageFormat = surfaceFormat.format;
    m_swapChainExtent = extent;
}

VkImageView HelloTriangleApplication::createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags)
{
    VkImageViewCreateInfo  viewInfo{};
    viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    viewInfo.image = image;
    viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;  // 图像的访问类型， 例如1D、2D、3D纹理
    viewInfo.format = format;   // 图像的访问格式
    //  用途、miplevel、3D立体多层设置
    viewInfo.subresourceRange.aspectMask = aspectFlags;  //  图像视口用途， 这里设备为颜色访问(可选择其它例如深度、模板等)
    viewInfo.subresourceRange.baseMipLevel = 0;  //  只使用miplevel 0
    viewInfo.subresourceRange.levelCount = 1;  //  只有一个miplevel
    viewInfo.subresourceRange.baseArrayLayer = 0;  //  只使用0层
    viewInfo.subresourceRange.layerCount = 1;   //  只有一个层，（多层一般用于3D眼镜之类的应用）

    //  图像的颜色映射（交换）或值固定(0、1)， 这里设置为默认映射
    viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
    viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
    viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
    viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;

    VkImageView imageView;
    if (vkCreateImageView(m_device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {
        throw std::runtime_error("failed to create texture image view!");
    }

    return imageView;
}

void HelloTriangleApplication::createImageViews()
{
    m_swapChainImageViews.resize(m_swapChainImages.size());

    for (uint32_t i = 0; i < m_swapChainImages.size(); i++) {
        m_swapChainImageViews[i] = createImageView(m_swapChainImages[i], m_swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT);
    }
}

void HelloTriangleApplication::createTextureImageView()
{
    m_textureImageView = createImageView(m_textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT);
}

void HelloTriangleApplication::createTextureSampler()
{
    VkSamplerCreateInfo samplerInfo{};
    samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
    samplerInfo.magFilter = VK_FILTER_LINEAR;  //  放大时的插值方式
    samplerInfo.minFilter = VK_FILTER_LINEAR;   //  缩小时的插值方式

    samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;  //  超出图像范围时的处理方式， 轴被称为 U、V 和 W，而不是 X、Y 和 Z。
    samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
    samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;

    samplerInfo.anisotropyEnable = VK_TRUE; //  是否使用各向异性过滤， 使用了可解决纹理欠采样的问题
    VkPhysicalDeviceProperties properties{};
    vkGetPhysicalDeviceProperties(m_physicalDevice, &properties);
    samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy;  // 限制了可用于计算最终颜色的纹素样本数量
    samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;  //  纹理边缘颜色
    samplerInfo.unnormalizedCoordinates = VK_FALSE;  //  是否禁用归一化坐标
    samplerInfo.compareEnable = VK_FALSE;   //  是否使用纹素值比较过滤功能
    samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;

    samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;  //  放大缩小插值模式
    samplerInfo.mipLodBias = 0.0f;
    samplerInfo.minLod = 0.0f;
    samplerInfo.maxLod = 0.0f;

    if (vkCreateSampler(m_device, &samplerInfo, nullptr, &m_textureSampler) != VK_SUCCESS) {
        throw std::runtime_error("failed to create texture sampler!");
    }
}

void HelloTriangleApplication::createRenderPass()
{
    /**
     * render pass的提出主要是为了移动端gpu。移动端gpu由于性能内存限制，普遍实现分方片渲染方式（就是把渲染大方形分割成一块块小方形多次渲染）。
     * 而render pass 指供的信息可提高分方块渲染的性能，例如指定多个子渲染通道说明子通道间的附件引用使得gpu可以分方块连接处理多个子通道而减少读写显存
     * render pass提供的详细信息同样也对桌面级gpu渲染有好处
    */

    //  帧缓冲附件经过渲染通道时如何进行内存布局处理、加载储存的模式描述
    VkAttachmentDescription colorAttachment{};
    colorAttachment.format = m_swapChainImageFormat;
    colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;  // 设置重采样
    //  附件渲染前后的操作， 用于颜色和深度缓冲
    //VK_ATTACHMENT_LOAD_OP_LOAD: Preserve the existing contents of the attachment
    //VK_ATTACHMENT_LOAD_OP_CLEAR: Clear the values to a constant at the start
    //VK_ATTACHMENT_LOAD_OP_DONT_CARE: Existing contents are undefined; we don't care about them
    //VK_ATTACHMENT_STORE_OP_STORE: Rendered contents will be stored in memory and can be read later(保存到帧缓冲中)
    //VK_ATTACHMENT_STORE_OP_DONT_CARE: Contents of the framebuffer will be undefined after the rendering operation
    colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
    colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;  //  本示例画一个三角形， 需要保存
    //  渲染前后模版缓冲处理， 与颜色和深度缓冲渲染处理设置相似
    colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    //   纹理或帧缓冲附件对应的 VkImage 例如内存中像素的layout有
    //VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: 图像被用于帧缓冲颜色附件
    //VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: 图像被用于swap chain中显示图像缓冲
    //VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: 图像被用于内存复制操作的目标。 等等
    colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;  // 渲染开始输入何种内存像素layout的image
    colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;  // 渲染结束应转换成输出何种内存像素layout的image

    //  深度缓冲附件经过渲染通道时如何进行内存布局处理、加载储存的模式描述
    VkAttachmentDescription depthAttachment{};
    depthAttachment.format = VK_FORMAT_D32_SFLOAT;
    depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
    depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
    depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

    /*****************子渲染通道设置************************/
    //   子渲染通道设置
    //  一个完整的渲染通道由多个子渲染通道构成，子渲染通道会被一个接一个连续执行
    //  子渲染通道构成完整的渲染可以节省内存带宽和提高性能

    // 每个子渲染通道都需要引用一个或多个帧缓冲器附件作为输出
    VkAttachmentReference colorAttachmentRef{};
    colorAttachmentRef.attachment = 0;   //  引用附件描述数组中哪个索引的附件描述
    colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;  //  帧缓冲附件在此子渲染通道所希望的内存像素布局, 让vulkan在子渲染开始时自动进行转换

    VkAttachmentReference depthAttachmentRef{};
    depthAttachmentRef.attachment = 1;
    depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

    //  子渲染通道描述
    VkSubpassDescription subpass{};
    subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;  //  绑定的管线类型， vulkan以后可能支持compute管线
    // 此数组中附件的索引直接从片段着色器引用，带有 layout(location = 0) out vec4 outColor 指令！
    subpass.colorAttachmentCount = 1;  //  有多少个颜色附件引用
    subpass.pColorAttachments = &colorAttachmentRef;
    subpass.pDepthStencilAttachment = &depthAttachmentRef;
    //    The following other types of attachments can be referenced by a VkSubpassDescription subpass:
    //        pInputAttachments: Attachments that are read from a shader
    //        pResolveAttachments: Attachments used for multisampling color attachments
    //        pDepthStencilAttachment: Attachment for depth and stencil data
    //        pPreserveAttachments: Attachments that are not used by this subpass, but for which the data must be preserved

    //  指定两个子通道之间的依赖关系
    //  说白了就是目标通道在什么管线阶段的什么访问状态阻塞，去等待源通道在什么管线阶段的什么访问状态的信号
    VkSubpassDependency dependency{};
    // VK_SUBPASS_EXTERNAL 指的是在渲染通道之前或之后的固有隐式子通道，具体取决于它是在 srcSubpass 还是 dstSubpass 中指定的
    //VK_SUBPASS_EXTERNAL表示给定渲染过程范围之外的任何内容。可能是指非本渲染通道的其它渲染。每个渲染通道都会隐含两个VK_SUBPASS_EXTERNAL，在开始和结束两端。
    //当用于 srcSubpass它指定了渲染过程之前发生的任何事情。当用于 dstSubpass它指定了渲染过程之后发生的任何事情。
    dependency.srcSubpass = VK_SUBPASS_EXTERNAL;    //  源子通道
    dependency.dstSubpass = 0;                      //  目标子通道, dstSubpass 必须始终高于 srcSubpass 以防止依赖图中出现循环(VK_SUBPASS_EXTERNAL除外)
    // 指定要等待的操作以及这些操作发生的阶段
    dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;  // 这里设置目标是为了确保交换链已经读取完图像
    dependency.srcAccessMask = 0;
    //结合上面两个参数， 是指： 目标子通道在管线进行颜色附件输出阶段写入颜色附件时阻塞，并等待源子通道在管线进行颜色附件输出阶段所有颜色附件操作完成时继续执行
    dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
    dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;

    //  创建渲染通道, 按子渲染通道的设置将渲染结果渲染到附件
    std::array<VkAttachmentDescription, 2> attachments = {colorAttachment, depthAttachment};
    VkRenderPassCreateInfo renderPassInfo{};
    renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
    renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());  //  附件数组
    renderPassInfo.pAttachments = attachments.data();
    renderPassInfo.subpassCount = 1;    //  子通道数组
    renderPassInfo.pSubpasses = &subpass;
    renderPassInfo.dependencyCount = 1;
    renderPassInfo.pDependencies = &dependency;

    if (vkCreateRenderPass(m_device, &renderPassInfo, nullptr, &m_renderPass) != VK_SUCCESS) {
        throw std::runtime_error("failed to create render pass!");
    }


}

void HelloTriangleApplication::createDescriptorSetLayout()
{
    //  描述符集布局绑定信息
    VkDescriptorSetLayoutBinding uboLayoutBinding{};
    uboLayoutBinding.binding = 0;       //  绑定号,对应着色器代码上的 layout(binding = ?)
    uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;  //  描述符对应的对象类型
    uboLayoutBinding.descriptorCount = 1;       //  数组长度， 一般用于数组
    uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;  //  绑定在管线的哪个着色阶段， 可多个标记逻辑或
    uboLayoutBinding.pImmutableSamplers = nullptr; // 图像采样相关描述符， 暂时用不到

    //  描述符集布局采样器绑定
    VkDescriptorSetLayoutBinding samplerLayoutBinding{};
    samplerLayoutBinding.binding = 1;
    samplerLayoutBinding.descriptorCount = 1;
    samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
    samplerLayoutBinding.pImmutableSamplers = nullptr;
    samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;  //  在哪个管线阶段使用

    std::array<VkDescriptorSetLayoutBinding, 2> bindings = {uboLayoutBinding, samplerLayoutBinding};

    //  描述符集布局创建信息
    VkDescriptorSetLayoutCreateInfo layoutInfo{};
    layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
    layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());                //  多少个绑定信息
    layoutInfo.pBindings = bindings.data();  //  绑定信息
    //  创建描述符集布局
    if (vkCreateDescriptorSetLayout(m_device, &layoutInfo, nullptr, &m_descriptorSetLayout) != VK_SUCCESS) {
        throw std::runtime_error("failed to create descriptor set layout!");
    }

}

void HelloTriangleApplication::createGraphicsPipeline()
{
    auto vertShaderCode = readFile(VERT_SPV_FILE);
    auto fragShaderCode = readFile(FRAG_SPV_FILE);

    //  着色器模块
    VkShaderModule vertShaderModule = createShaderModule(m_device, vertShaderCode);
    VkShaderModule fragShaderModule = createShaderModule(m_device, fragShaderCode);

    VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
    vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; //  着色器要被用于图形管线的什么阶段
    vertShaderStageInfo.module = vertShaderModule;
    vertShaderStageInfo.pName = "main";             // 着色器主函数
    // pSpecializationInfo : 它允许您指定着色器常量的值

    VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
    fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
    fragShaderStageInfo.module = fragShaderModule;
    fragShaderStageInfo.pName = "main";

    VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};

    //  顶数据输入格式，主要设置顶点数据的间隔、逐顶结构或逐实例结构、附加属性传递。
    VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
    vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
    auto bindingDescription = Vertex::getBindingDescription();
    auto attributeDescriptions = Vertex::getAttributeDescriptions();
    vertexInputInfo.vertexBindingDescriptionCount = 1;
    vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
    vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
    vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();

    //  声明输入的顶点数据被用于绘制何种图原，
    VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
    inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
    //VK_PRIMITIVE_TOPOLOGY_POINT_LIST: 画多个点
    //VK_PRIMITIVE_TOPOLOGY_LINE_LIST: 画多条线，每两个顶点为一条线, 线没有复用的顶点
    //VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: 画多条头尾连接的线，每两个顶点为一条线, 线有复用的顶点
    //VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: 画多个三角形， 没有复用的顶点
    //VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: 画多个三角形， 有一个复用的顶点
    inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
    //  primitiveRestartEnable设置为True, 表示可使用索引缓冲的特殊索引(例如0xFFFF or 0xFFFFFFFF)打断连接的线或三角形
    inputAssembly.primitiveRestartEnable = VK_FALSE;


    // 视口大小, 从swap chain 图像到帧缓冲的转换（有缩放拉伸的特征）
    VkViewport viewport{};
    viewport.x = 0.0f;
    viewport.y = 0.0f;
    viewport.width = static_cast<float>(m_swapChainExtent.width);
    viewport.height = static_cast<float>(m_swapChainExtent.height);
    viewport.minDepth = 0.0f;  // 可视深度范围
    viewport.maxDepth = 1.0f;

    //  剪裁大小， 声明swap chain图像哪些范围内的像素可以被管线方格处理阶段处理
    VkRect2D scissor{};
    scissor.offset = {0, 0};
    scissor.extent = m_swapChainExtent;

    //    图形管线一些状态值可在绘图时动态调整， （例如视口尺寸、线宽、混合常数、深度边界）
    std::vector<VkDynamicState> dynamicStates = {
        VK_DYNAMIC_STATE_VIEWPORT,  //  视口尺寸
        VK_DYNAMIC_STATE_SCISSOR    //  视口剪裁
    };

    VkPipelineDynamicStateCreateInfo dynamicState{};
    dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
    dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
    dynamicState.pDynamicStates = dynamicStates.data();

    //  声明视口、剪裁的数量
    //  使用动态状态， 甚至可以在单个命令缓冲中指定不同的视口剪裁范围; 使用多视口要在创建逻辑设备时声明相应的扩展特性
    VkPipelineViewportStateCreateInfo viewportState{};
    viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
    viewportState.viewportCount = 1;
    viewportState.scissorCount = 1;

    /*
     * 不可变视口和前载范围的声明
    VkPipelineViewportStateCreateInfo viewportState{};
    viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
    viewportState.viewportCount = 1;
    viewportState.pViewports = &viewport;  //  静态不可变要设置好指针
    viewportState.scissorCount = 1;
    viewportState.pScissors = &scissor;
    */


    //  方格化阶段信息声明； 方格化在片段着色器前一级
    //  可指定深度测试、面方向测试、剪裁测试， 也可指定只画多边形的线或画完线填充
    VkPipelineRasterizationStateCreateInfo rasterizer{};
    rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
    rasterizer.depthClampEnable = VK_FALSE;  //  超出深度范围是否钳位或丢弃， 这里先设置为丢弃
    rasterizer.rasterizerDiscardEnable = VK_FALSE;  // 设置为true相当于禁止任何输出到下一个着色阶段
    //  多边形的方格化模式,
    //VK_POLYGON_MODE_FILL: fill the area of the polygon with fragments
    //VK_POLYGON_MODE_LINE: polygon edges are drawn as lines
    //VK_POLYGON_MODE_POINT: polygon vertices are drawn as points
    //Using any mode other than fill requires enabling a GPU feature.
    rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
    //  设置线宽，大于1.0f的值需要打开相应gpu特性
    rasterizer.lineWidth = 1.0f;
    //  正反面剔除设置
    rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
//    rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
    rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;  //  由于vulkan与opengl图像坐标上下翻转， 逆时针方向为面正方向
    //   深度值的一些偏移设置, 一般用于生成立体阴影
    rasterizer.depthBiasEnable = VK_FALSE;
    rasterizer.depthBiasConstantFactor = 0.0f; // Optional
    rasterizer.depthBiasClamp = 0.0f; // Optional
    rasterizer.depthBiasSlopeFactor = 0.0f; // Optional

    //  片段着色器多重采样信息， 一般用于抗锯齿; 暂时禁用
    VkPipelineMultisampleStateCreateInfo multisampling{};
    multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
    multisampling.sampleShadingEnable = VK_FALSE;
    multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
    multisampling.minSampleShading = 1.0f; // Optional
    multisampling.pSampleMask = nullptr; // Optional
    multisampling.alphaToCoverageEnable = VK_FALSE; // Optional
    multisampling.alphaToOneEnable = VK_FALSE; // Optional

    //  深度和模板测试： VkPipelineDepthStencilStateCreateInfo

    //  每个帧缓冲单独设置的颜色混合信息
    //  可设置当前绘制与上次绘制的帧缓冲颜色混合模式
    VkPipelineColorBlendAttachmentState colorBlendAttachment{};   // VkPipelineColorBlendStateCreateInfo 用于全局颜色混合设定
    colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
            VK_COLOR_COMPONENT_G_BIT |
            VK_COLOR_COMPONENT_B_BIT |
            VK_COLOR_COMPONENT_A_BIT;
    colorBlendAttachment.blendEnable = VK_FALSE;  // 设置为false使片段着色器的输出直通到帧缓冲
    colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Optional
    colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional
    colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional
    colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Optional
    colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional
    colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional

    //  全局颜色混合信息
    //  合并多个VkPipelineColorBlendAttachmentState信息， 并设置混合常量
    VkPipelineColorBlendStateCreateInfo colorBlending{};
    colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
    colorBlending.logicOpEnable = VK_FALSE;  // 是否使用位逻辑操作帧缓冲区
    colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional  直接复制
    colorBlending.attachmentCount = 1;
    colorBlending.pAttachments = &colorBlendAttachment;
    colorBlending.blendConstants[0] = 0.0f; // Optional
    colorBlending.blendConstants[1] = 0.0f; // Optional
    colorBlending.blendConstants[2] = 0.0f; // Optional
    colorBlending.blendConstants[3] = 0.0f; // Optional

    //  管线布局器创建
    VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
    pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
    pipelineLayoutInfo.setLayoutCount = 1;
    pipelineLayoutInfo.pSetLayouts = &m_descriptorSetLayout; // 创建管线时使用的描述符布局
    pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional
    pipelineLayoutInfo.pPushConstantRanges = nullptr; // Optional

    if (vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &m_pipelineLayout) != VK_SUCCESS) {
        throw std::runtime_error("failed to create pipeline layout!");
    }

    // 深度测试设置
    VkPipelineDepthStencilStateCreateInfo depthStencil{};
    depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
    depthStencil.depthTestEnable = VK_TRUE;
    depthStencil.depthWriteEnable = VK_TRUE;
    depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
    depthStencil.depthBoundsTestEnable = VK_FALSE; //  是否超出范围就不绘画
    depthStencil.minDepthBounds = 0.0f; // Optional
    depthStencil.maxDepthBounds = 1.0f; // Optional
    depthStencil.stencilTestEnable = VK_FALSE;
    depthStencil.front = {}; // Optional
    depthStencil.back = {}; // Optional

    //  创建管线的信息
    VkGraphicsPipelineCreateInfo pipelineInfo{};
    pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
    pipelineInfo.stageCount = 2;  //  有两个可编程阶段
    pipelineInfo.pStages = shaderStages;
    pipelineInfo.pVertexInputState = &vertexInputInfo;  //  顶点及其属性输入的绑定模式
    pipelineInfo.pInputAssemblyState = &inputAssembly;  //  顶点组装绘画模式
    pipelineInfo.pViewportState = &viewportState;   //  视口和剪裁设定
    pipelineInfo.pRasterizationState = &rasterizer;  // 方格化阶段指定深度测试、面方向测试、剪裁测试、线宽、多段线是否填充等
    pipelineInfo.pMultisampleState = &multisampling;   //  多重采样去除锯齿信息
    pipelineInfo.pDepthStencilState = &depthStencil; // Optional  // 深度和模板状态
    pipelineInfo.pColorBlendState = &colorBlending;     //  颜色混合模式
    pipelineInfo.pDynamicState = &dynamicState;         //  声明那些固定功能状态信息能被动态调整
    pipelineInfo.layout = m_pipelineLayout;        //  输入uniform常量的布局
    pipelineInfo.renderPass = m_renderPass;         //  渲染通道（如何识别和处理帧缓冲中的附件)
    pipelineInfo.subpass = 0;       //  管线要使用的子渲染通道索引，切换索引使用不同的附件渲染模式
    //  用于图形管线继承。 也就是说本次创建的管线可以派生于某个现有管线
    //  需要设置flags成员的VK_PIPELINE_CREATE_DERIVATIVE_BIT标记位
    //    pipelineInfo.flags;
    pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional  //  继承哪个现有管线对象句柄
    pipelineInfo.basePipelineIndex = -1; // Optional            //  继承哪个即将创建的管线索引

    //  创建管线对象
    // vkCreateGraphicsPipelines可用于一次创建多个管线对象
    //  第二个参数为管线缓存对象，可用于加快管理的创建速度，详细请参考相关资料
    if (vkCreateGraphicsPipelines(m_device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &m_graphicsPipeline) != VK_SUCCESS) {
        throw std::runtime_error("failed to create graphics pipeline!");
    }


    //  着色器模块只是spir-v二进制节字码的一个轻量封装， 创建图形管线过程会把它转换成gpu代码， 所以创建管线后可以释放它的内存
    vkDestroyShaderModule(m_device, fragShaderModule, nullptr);
    vkDestroyShaderModule(m_device, vertShaderModule, nullptr);
}

void HelloTriangleApplication::createFramebuffers()
{
    //  帧缓冲数量
    m_swapChainFramebuffers.resize(m_swapChainImageViews.size());

    for (size_t i = 0; i < m_swapChainImageViews.size(); i++) {
        std::array<VkImageView, 2> attachments = {
            m_swapChainImageViews[i],
            m_depthImageView
        };

        VkFramebufferCreateInfo framebufferInfo{};
        framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
        //  指定需要兼容的渲染通道， 如何识别处理、前置处理、后置处理附件的内存
        //  渲染通道的附件类型和数量应与帧缓冲的附件相同
        framebufferInfo.renderPass = m_renderPass;
        framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size()); //  输出附件数量
        framebufferInfo.pAttachments = attachments.data();     //  将会绑定到渲染通道VkRenderPassCreateInfo的pAttachment数组对应附件
        framebufferInfo.width = m_swapChainExtent.width;
        framebufferInfo.height = m_swapChainExtent.height;
        framebufferInfo.layers = 1;   //  多少层图像

        if (vkCreateFramebuffer(m_device, &framebufferInfo, nullptr, &m_swapChainFramebuffers[i]) != VK_SUCCESS) {
            throw std::runtime_error("failed to create framebuffer!");
        }
    }
}

void HelloTriangleApplication::createCommandPool()
{
    VkCommandPoolCreateInfo poolInfo{};
    poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
    //  VK_COMMAND_POOL_CREATE_TRANSIENT_BIT : 提示命令缓冲区经常用新命令重新记录（可能会改变内存分配行为）
    //  VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT : 允许单独重新记录命令缓冲区，如果没有此标志，它们都必须一起重置
    //我们将每帧记录一个命令缓冲区，因此我们希望能够重置并重新记录它。
    //因此，我们需要为我们的命令池设置 VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT 标志位。
    poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
    poolInfo.queueFamilyIndex = m_devQueFamilies[QueFamiliesKey::Graphics];  // 命令池中的命令被发送到的目标队列

    //  创建命令池
    if (vkCreateCommandPool(m_device, &poolInfo, nullptr, &m_commandPool) != VK_SUCCESS) {
        throw std::runtime_error("failed to create command pool!");
    }
}

VkCommandBuffer HelloTriangleApplication::beginSingleTimeCommands()
{
    VkCommandBufferAllocateInfo allocInfo{};
    allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
    allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
    allocInfo.commandPool = m_commandPool;  // // 创建一个有VK_COMMAND_POOL_CREATE_TRANSIENT_BIT标记的单独的命令池，可能能够应用内存分配优化。
    allocInfo.commandBufferCount = 1;

    //  创建一个专用的命令缓冲，执行cpu-gpu间数据收发
    VkCommandBuffer commandBuffer;
    vkAllocateCommandBuffers(m_device, &allocInfo, &commandBuffer);

     //  记录传输命令
    VkCommandBufferBeginInfo beginInfo{};
    beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
    //  开始记录命令到缓冲
    vkBeginCommandBuffer(commandBuffer, &beginInfo);

    return commandBuffer;
}

void HelloTriangleApplication::endSingleTimeCommands(VkCommandBuffer commandBuffer)
{
    //  结束记录命令到缓冲
    vkEndCommandBuffer(commandBuffer);

     //  发送命令缓冲中的命令到队列
    VkSubmitInfo submitInfo{};
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &commandBuffer;

    vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
    vkQueueWaitIdle(m_graphicsQueue);  //  等待队列执行完毕，进入空闲状态

    //  打完斋不要和尚， 这个临时的命令缓冲只是执行一次复制，这里把它销毁
    vkFreeCommandBuffers(m_device, m_commandPool, 1, &commandBuffer);
}

uint32_t HelloTriangleApplication::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
{
    VkPhysicalDeviceMemoryProperties memProperties;
    vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memProperties);  // 查询显存属性
    for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
        if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
            return i;
        }
    }

    throw std::runtime_error("failed to find suitable memory type!");
}

void HelloTriangleApplication::createBuffer(VkDeviceSize size,
                                            VkBufferUsageFlags usage, VkMemoryPropertyFlags properties,
                                            VkBuffer &buffer, VkDeviceMemory &bufferMemory)
{
    //  gpu缓冲区创建信息
    VkBufferCreateInfo bufferInfo{};
    bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    bufferInfo.flags = 0; //  可用于创建稀疏内存缓冲区（多个不同地址合并成一块缓冲区）, 目前设置为0
    bufferInfo.size = size;  //  缓冲区字节长度
    bufferInfo.usage = usage;  // 缓冲区用用途
    bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; // 不同队列间的共享属性， 这里使用独占访问

    if (vkCreateBuffer(m_device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) {
        throw std::runtime_error("failed to create vertex buffer!");
    }

    //  缓冲区对象创建之后是没有显存的， 要为它申请显存
    //VkMemoryRequirements成员信息
    //size: 占用的显存字节数
    //alignment: 分配到的显存地址字节对齐
    //memoryTypeBits: 适合缓冲区的内存类型的位字段, 可用于查找适合的显存类型
    VkMemoryRequirements memRequirements;
    vkGetBufferMemoryRequirements(m_device, buffer, &memRequirements);  //  查询缓冲对象要占用的显存大小

    //  申请显存信息
    //不应该为每个单独的缓冲区实际调用 vkAllocateMemory。
    //同时分配内存的最大数量受到 maxMemoryAllocationCount 物理设备限制的限制，
    //即使在 NVIDIA GTX 1080 等高端硬件上也可能低至 4096。
    //同时为大量对象分配内存的正确方法是创建一个自定义分配器，
    //通过使用我们在许多函数中看到的偏移参数在许多不同对象之间拆分单个分配。
    //您可以自己实现这样的分配器，也可以使用 GPUOpen 倡议提供的 VulkanMemoryAllocator 库。
    //但是，对于本教程，可以为每个资源使用单独的分配，因为我们现在不会接近达到这些限制中的任何一个。
    VkMemoryAllocateInfo allocInfo{};
    allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    allocInfo.allocationSize = memRequirements.size;
    //VK_MEMORY_PROPERTY_HOST_COHERENT_BIT标志位保证主机侧映射时内存不会被cache
    // 写入映射内存后调用vkFlushMappedMemoryRanges，读取映射内存前调用vkInvalidateMappedMemoryRange也可以保证映射的显存可以被立即刷新
    allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits,
                                               properties  //  可映射到主机内存的一致性显存
                                               );
    if (vkAllocateMemory(m_device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) {
        throw std::runtime_error("failed to allocate vertex buffer memory!");
    }

    //  将显存绑定到顶点缓冲对象
    vkBindBufferMemory(m_device, buffer, bufferMemory,
                       0  // 如果偏移量不为零,那么它需要被 memRequirements.alignment 整除
                       );

}

void HelloTriangleApplication::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
{
    VkCommandBuffer commandBuffer = beginSingleTimeCommands();

    //  通道输入缓冲对象复制命令
    VkBufferCopy copyRegion{};
    copyRegion.srcOffset = 0; // Optional
    copyRegion.dstOffset = 0; // Optional
    copyRegion.size = size;  // 必须指定实际大小，不能使用VK_WHOLE_SIZE
    vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);

    endSingleTimeCommands(commandBuffer);
}

void HelloTriangleApplication::transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout)
{
    VkCommandBuffer commandBuffer = beginSingleTimeCommands();

    //  图像缓冲对象内存操作关卡， 用于同步图像缓冲对象内存操作完成
    VkImageMemoryBarrier barrier{};
    barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    barrier.oldLayout = oldLayout;
    barrier.newLayout = newLayout;
    barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;  //  这两个是用于转移图像缓冲对象的队列所有权用的， VK_QUEUE_FAMILY_IGNORED是不进行转移的意思
    barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrier.image = image;
    barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; //  指定图像缓冲对象是用于颜色、深度、模版等方面
    barrier.subresourceRange.baseMipLevel = 0;
    barrier.subresourceRange.levelCount = 1;
    barrier.subresourceRange.baseArrayLayer = 0;
    barrier.subresourceRange.layerCount = 1;

    //指定哪些类型的涉及资源的操作必须在屏障之前发生，哪些涉及资源的操作必须在屏障上等待
    barrier.srcAccessMask = 0; // TODO
    barrier.dstAccessMask = 0; // TODO

    VkPipelineStageFlags sourceStage;
    VkPipelineStageFlags destinationStage;

    if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
        barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
        //        if (hasStencilComponent(format)) {
        //            barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
        //        }
    } else {
        barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    }

    if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
        barrier.srcAccessMask = 0;  //  不关心源布局，写入不必等待任何东西
        barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;  //  等待转换写完成

        sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;  //  VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT这里是指管线入口阶段
        destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;      // 目标转换的传输伪管线阶段，等待操作
    } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
        barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; //  等待转换写完成
        barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; //  等待图像缓冲对象在着色器中可读

        sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;    // 源转换的传输伪管线阶段，等待操作
        destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;   // 目标转换的片段着色器管线阶段，等待操作
    } else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
        barrier.srcAccessMask = 0;
        barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;

        sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
        destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
    } else {
        throw std::invalid_argument("unsupported layout transition!");
    }

    vkCmdPipelineBarrier(
                commandBuffer,
                sourceStage,        //  屏障之前在什么管线阶段等待访问完成
                destinationStage,   //  屏障在什么阶段等待访问完成
                0,
                0, nullptr,
                0, nullptr,
                1, &barrier
                );

    endSingleTimeCommands(commandBuffer);
}

void HelloTriangleApplication::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
{
    VkCommandBuffer commandBuffer = beginSingleTimeCommands();
    //  要复制的区域
    VkBufferImageCopy region{};
    region.bufferOffset = 0;
    // bufferRowLength bufferImageHeight以texel为单位指定缓冲存储器中较大的二维或三维图像的子区域，并控制寻址计算。
    // 如果这些值中的任何一个为零，则根据imageExtent，缓冲存储器的这一方面被认为是紧密压缩的
    region.bufferRowLength = 0;
    region.bufferImageHeight = 0;

    region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    region.imageSubresource.mipLevel = 0;
    region.imageSubresource.baseArrayLayer = 0;
    region.imageSubresource.layerCount = 1;

    region.imageOffset = {0, 0, 0};
    region.imageExtent = {
        width,
        height,
        1
    };

    //  拷贝
    vkCmdCopyBufferToImage(
        commandBuffer,
        buffer,
        image,
        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
        1,
        &region
    );

    endSingleTimeCommands(commandBuffer);
}

void HelloTriangleApplication::createImage(uint32_t width, uint32_t height,
                                           VkFormat format, VkImageTiling tiling,
                                           VkImageUsageFlags usage, VkMemoryPropertyFlags properties,
                                           VkImage &image, VkDeviceMemory &imageMemory)
{
    VkImageCreateInfo imageInfo{};
    imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
    imageInfo.imageType = VK_IMAGE_TYPE_2D;  //   指定的图像类型告诉 Vulkan 图像中的纹素将使用哪种坐标系来处理。
    imageInfo.extent.width = width;  // 宽
    imageInfo.extent.height = height;  // 高
    imageInfo.extent.depth = 1;     //  深
    imageInfo.mipLevels = 1;  //mip级数
    imageInfo.arrayLayers = 1;  //  图像缓冲对象是数组时，其数组长度
    // 使用非常见的格式可能需要烦人的转换。
    imageInfo.format = format;  // 图像缓冲对象显存中的数据格式。 注意，gpu驱动不一定都支持所有这些格式
    //VK_IMAGE_TILING_LINEAR: 纹素是以行优先作排列布局. 如果希望能够直接访问图像内存中的纹素，那么必须使用这个
    //  如果使用这个VK_IMAGE_TILING_LINEAR，就不用进列优先转换成行优先了
    //VK_IMAGE_TILING_OPTIMAL: 纹素按实现定义的顺序排列，以实现最佳访问
    imageInfo.tiling = tiling;  //  方块重排优化
    //    VK_IMAGE_LAYOUT_UNDEFINED：GPU 不能直接使用，首次转换将丢弃纹素。
    //    VK_IMAGE_LAYOUT_PREINITIALIZED：GPU 不能直接使用，着次转换将保留纹素。
    imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;  // 初始化时的布局，根据tiling的值而定
    imageInfo.usage = usage;  // 用途
    imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; // 队列间共享
    imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;  //  重采样次数
    imageInfo.flags = 0; // 疏散图像内存组织图像时使用，例如申请大型3d显存时占用连续内存时影响gpu的显存交换效率

    //  创建图像缓冲对象
    if (vkCreateImage(m_device, &imageInfo, nullptr, &image) != VK_SUCCESS) {
        throw std::runtime_error("failed to create image!");
    }

     //  图像缓冲对象对应的显存申请分配信息
    VkMemoryRequirements memRequirements;
    vkGetImageMemoryRequirements(m_device, image, &memRequirements);

    VkMemoryAllocateInfo allocInfo{};
    allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    allocInfo.allocationSize = memRequirements.size;
    allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);

    //  图像缓冲对象对应的显存申请
    if (vkAllocateMemory(m_device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
        throw std::runtime_error("failed to allocate image memory!");
    }

    //  图像缓冲对象和对应的显存绑定
    vkBindImageMemory(m_device, image, imageMemory, 0);
}

void HelloTriangleApplication::createDepthResources()
{
    VkFormat depthFormat = VK_FORMAT_D32_SFLOAT;        //  教程有多种格式自动择优选取的方法， 这里直接设置为最通用的
    createImage(m_swapChainExtent.width, m_swapChainExtent.height,
                depthFormat,
                VK_IMAGE_TILING_OPTIMAL,
                VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
                VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
                m_depthImage, m_depthImageMemory);
    m_depthImageView = createImageView(m_depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT);

    // 下面的内存是显式把深度缓冲图转换布局， 由于渲染时会自动处理这样的问题， 所以不是必须的

    transitionImageLayout(m_depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
}

void HelloTriangleApplication::createTextureImage()
{
    //  使用stb库读取纹理源图像文件
    int texWidth, texHeight, texChannels;
    stbi_uc* pixels = stbi_load("texture.jpg", &texWidth, &texHeight, &texChannels, STBI_rgb_alpha); // 强制生成rgba通道
    VkDeviceSize imageSize = static_cast<VkDeviceSize>(texWidth * texHeight * 4) ;

    if (!pixels) {
        throw std::runtime_error("failed to load texture image!");
    }

    //  暂存缓冲区对象， 用于将数据会输到仅显卡可见的显存
    VkBuffer stagingBuffer;
    VkDeviceMemory stagingBufferMemory;
    createBuffer(imageSize,
                 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
                 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
                 stagingBuffer, stagingBufferMemory);
    void* data;  // 图像文件的数据先放到暂存缓冲区对象中
    vkMapMemory(m_device, stagingBufferMemory, 0, imageSize, 0, &data);
    memcpy(data, pixels, static_cast<size_t>(imageSize));
    vkUnmapMemory(m_device, stagingBufferMemory);

    stbi_image_free(pixels);  // 拷贝完， 图像文件数据没有用了，把它干掉

    //  创建图像缓冲对象、分配显存、绑定
    createImage(static_cast<uint32_t>(texWidth),
                static_cast<uint32_t>(texHeight),
                VK_FORMAT_R8G8B8A8_SRGB,
                VK_IMAGE_TILING_OPTIMAL,
                VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
                VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
                m_textureImage, m_textureImageMemory);

    //  转换图像缓冲对象的内存布局
    transitionImageLayout(m_textureImage, VK_FORMAT_R8G8B8A8_SRGB,
                          VK_IMAGE_LAYOUT_UNDEFINED,  //  图像是使用 VK_IMAGE_LAYOUT_UNDEFINED 布局创建的，因此应将其指定为旧布局，因为在执行复制操作之前我们不关心它的内容所以这样设置
                          VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL  //  传输最优化， 使其以最优性能传输
                          );
    copyBufferToImage(stagingBuffer, m_textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
    //  拷贝完再次转换布局， 使得着色器可以采样纹理图像
    transitionImageLayout(m_textureImage, VK_FORMAT_R8G8B8A8_SRGB,
                          VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                          VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL  //  着色器只读优化
                          );

    //  释放暂存缓冲对象
    vkDestroyBuffer(m_device, stagingBuffer, nullptr);
    vkFreeMemory(m_device, stagingBufferMemory, nullptr);
}

void HelloTriangleApplication::createVertexBuffer()
{
    VkDeviceSize bufferSize = sizeof(m_vertices[0]) * m_vertices.size();


    VkBuffer stagingBuffer;         //  暂存输入缓冲对象
    VkDeviceMemory stagingBufferMemory;
    //VK_MEMORY_PROPERTY_HOST_COHERENT_BIT标志位保证主机侧映射时内存不会被cache
    createBuffer(bufferSize,
                 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,  //  这里的用途是用于cpu-gpu间传输的源
                 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
                 stagingBuffer, stagingBufferMemory);

    //  将显存映射到cpu内存， 方便数据传递
    //  将数据传输到 GPU 是在后台发生的操作，规范只是保证在下次调用 vkQueueSubmit 时完成，复制的显存对GPU可见。
    void* data = nullptr;
    vkMapMemory(m_device, stagingBufferMemory,
                0,  //  偏移
                bufferSize,  //  要映射的大小， VK_WHOLE_SIZE 表示映射整段显存
                0,
                &data);
    // 写入映射内存后调用vkFlushMappedMemoryRanges，读取映射内存前调用vkInvalidateMappedMemoryRange也可以保证映射的显存可以被立即刷新
    memcpy(data, m_vertices.data(), static_cast<size_t>(bufferSize));  // 拷贝数据
    vkUnmapMemory(m_device, stagingBufferMemory);   // 取消映射

    //  创建cpu侧不能映射的gpu专用显存
    createBuffer(bufferSize,
                 VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,  //  这里的用途是用于cpu-gpu间传输的目标，而且用作顶点缓冲对象
                 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,   // VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT 表示专属于gpu的显存，一般不能被cpu映射访问（性能更优）
                 m_vertexBuffer, m_vertexBufferMemory);

    //  将暂存缓冲对象的数据传输到专用缓冲对象中
    copyBuffer(stagingBuffer, m_vertexBuffer, bufferSize);
    //  暂存缓冲对象的使命完成了，可释放它和它的内存了
    vkDestroyBuffer(m_device, stagingBuffer, nullptr);
    vkFreeMemory(m_device, stagingBufferMemory, nullptr);

}

void HelloTriangleApplication::createIndexBuffer()
{
    VkDeviceSize bufferSize = sizeof(m_indices[0]) * m_indices.size();

    VkBuffer stagingBuffer; //  暂存缓冲对象
    VkDeviceMemory stagingBufferMemory;
    createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
                 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
                 stagingBuffer, stagingBufferMemory);

    void* data;
    vkMapMemory(m_device, stagingBufferMemory, 0, bufferSize, 0, &data);
    memcpy(data, m_indices.data(), static_cast<size_t>(bufferSize));
    vkUnmapMemory(m_device, stagingBufferMemory);

    createBuffer(bufferSize,
                 VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,  //  VK_BUFFER_USAGE_INDEX_BUFFER_BIT专用于索引输入对象
                 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
                 m_indexBuffer, m_indexBufferMemory);

    copyBuffer(stagingBuffer, m_indexBuffer, bufferSize);

    vkDestroyBuffer(m_device, stagingBuffer, nullptr);
    vkFreeMemory(m_device, stagingBufferMemory, nullptr);
}

void HelloTriangleApplication::createUniformBuffers()
{
    VkDeviceSize bufferSize = sizeof(UniformBufferObject);

    m_uniformBuffers.resize(MAX_FRAMES_IN_FLIGHT);
    m_uniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT);

    for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
        createBuffer(bufferSize,
                     VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,  //  用于统一缓冲对象
                     VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,  // gpu可见的， cpu可映射的一致显存
                     m_uniformBuffers[i], m_uniformBuffersMemory[i]);
    }

}

void HelloTriangleApplication::createDescriptorPool()
{
    //  描述符池的大小（可以容纳的描述符个数）
    std::array<VkDescriptorPoolSize, 2> poolSizes{};
    poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    poolSizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
    poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
    poolSizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);

    // 描述符池的创建信息
    VkDescriptorPoolCreateInfo poolInfo{};
    poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
    poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
    poolInfo.pPoolSizes = poolSizes.data();
    poolInfo.maxSets = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);  //可以分配的描述符集的最大数量
    // VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT 标志可以是否可以释放单个描述符集

    if (vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &m_descriptorPool) != VK_SUCCESS) {
        throw std::runtime_error("failed to create descriptor pool!");
    }
}

void HelloTriangleApplication::createDescriptorSets()
{
    //  描述符池申请信息
    std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, m_descriptorSetLayout);
    VkDescriptorSetAllocateInfo allocInfo{};
    allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
    allocInfo.descriptorPool = m_descriptorPool;  //  从哪个描述符池创建描述符集
    allocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT); // 创建的描述符池数量
    allocInfo.pSetLayouts = layouts.data(); // 布局信息

    //  申请描述符集
    m_descriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
    if (vkAllocateDescriptorSets(m_device, &allocInfo, m_descriptorSets.data()) != VK_SUCCESS) {
        throw std::runtime_error("failed to allocate descriptor sets!");
    }
    //
    for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {  //  配置描述符
        VkDescriptorBufferInfo bufferInfo{};
        bufferInfo.buffer = m_uniformBuffers[i];
        bufferInfo.offset = 0;
        bufferInfo.range = sizeof(UniformBufferObject);

        VkDescriptorImageInfo imageInfo{};
        imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
        imageInfo.imageView = m_textureImageView;
        imageInfo.sampler = m_textureSampler;

        std::array<VkWriteDescriptorSet, 2> descriptorWrites{};

        descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
        descriptorWrites[0].dstSet = m_descriptorSets[i];  //  写入的目标描述符集
        descriptorWrites[0].dstBinding = 0;             //  写入的目标绑定号
        descriptorWrites[0].dstArrayElement = 0;    // 写入数组第一个元素
        descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;   //  描述符类型
        descriptorWrites[0].descriptorCount = 1;  //  要更新的描述符数量
        descriptorWrites[0].pBufferInfo = &bufferInfo;  //  引用的缓冲对象

        descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
        descriptorWrites[1].dstSet = m_descriptorSets[i];
        descriptorWrites[1].dstBinding = 1;
        descriptorWrites[1].dstArrayElement = 0;
        descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        descriptorWrites[1].descriptorCount = 1;
        descriptorWrites[1].pImageInfo = &imageInfo;  // 引用的图像对象和纹理视图信息
        descriptorWrites[1].pTexelBufferView = nullptr; // 引用的纹理对象

        //  更新描述符集
        vkUpdateDescriptorSets(m_device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
    }
}

void HelloTriangleApplication::createCommandBuffer()
{
    m_commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
    VkCommandBufferAllocateInfo allocInfo{};
    allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
    allocInfo.commandPool = m_commandPool;  //  命令缓冲作用的命令池
    //  命令缓冲的级别
    //VK_COMMAND_BUFFER_LEVEL_PRIMARY：可以提交到队列执行，但不能从其他命令缓冲区调用。
    //VK_COMMAND_BUFFER_LEVEL_SECONDARY：不能直接提交，但可以从主命令缓冲区调用。有助于重用主要命令缓冲区中的常见操作。
    allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
    allocInfo.commandBufferCount = static_cast<uint32_t>(m_commandBuffers.size());  //  要创建的命令缓冲数量

    if (vkAllocateCommandBuffers(m_device, &allocInfo, m_commandBuffers.data()) != VK_SUCCESS) {
        throw std::runtime_error("failed to allocate command buffers!");
    }
}

void HelloTriangleApplication::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
{
    //  命令开始信息, 声明即将要记录一条新的命令
    VkCommandBufferBeginInfo beginInfo{};
    beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    beginInfo.flags = 0; // Optional  //  这条命令的用途
    beginInfo.pInheritanceInfo = nullptr; // Optional pInheritanceInfo 参数仅与辅助命令缓冲区相关。它指定从调用主命令缓冲区继承的状态。

    //如果命令缓冲区已经记录过一次，则调用 vkBeginCommandBuffer 将隐式重置它。以后不可能将命令附加到缓冲区。
    if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
        throw std::runtime_error("failed to begin recording command buffer!");
    }

    //  渲染通道开始信息
    VkRenderPassBeginInfo renderPassInfo{};
    renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
    renderPassInfo.renderPass = m_renderPass;  //  要开始的渲染通道
    renderPassInfo.framebuffer = m_swapChainFramebuffers[imageIndex];  //  渲染的目标帧缓冲
    //  渲染区域定义着色器加载和存储将发生的位置。此区域之外的像素将具有未定义的值。它应该与附件的大小相匹配以获得最佳性能。
    renderPassInfo.renderArea.offset = {0, 0};
    renderPassInfo.renderArea.extent = m_swapChainExtent;

    //  颜色复位值信息。 对应于VK_ATTACHMENT_LOAD_OP_CLEAR
    // clearValues 的顺序应与附件的顺序相同。
    std::array<VkClearValue, 2> clearValues{};
    clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
    clearValues[1].depthStencil = {1.0f, 0};
    renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
    renderPassInfo.pClearValues = clearValues.data();


    //  渲染通道开始
    //最后一个参数控制如何提供渲染过程中的绘图命令。
    //VK_SUBPASS_CONTENTS_INLINE：渲染通道命令将嵌入主命令缓冲区本身，不会执行辅助命令缓冲区。
    //VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS：渲染通道命令将从辅助命令缓冲区执行。
    vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);

    //  绑定命令作用的目标管线
    vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_graphicsPipeline);

    // 动态设定一些渲染状态信息
    VkViewport viewport{};
    viewport.x = 0.0f;
    viewport.y = 0.0f;
    viewport.width = static_cast<float>(m_swapChainExtent.width);
    viewport.height = static_cast<float>(m_swapChainExtent.height);
    viewport.minDepth = 0.0f;
    viewport.maxDepth = 1.0f;
    vkCmdSetViewport(commandBuffer, 0, 1, &viewport);

    VkRect2D scissor{};
    scissor.offset = {0, 0};
    scissor.extent = m_swapChainExtent;
    vkCmdSetScissor(commandBuffer, 0, 1, &scissor);

    //  描述符集绑定
    vkCmdBindDescriptorSets(commandBuffer,
                            VK_PIPELINE_BIND_POINT_GRAPHICS,  //描述符集不只是图形管线可用的，计算管线也可以用
                            m_pipelineLayout,  //  与之对应的管线布局
                            0,  //  描述符集数组着个元素对应glsl layout(set = ?,...)的set
                            1,  //  描述符集数组有多少个描述符集
                            &m_descriptorSets[m_currentFrame],
                            0,
                            nullptr
                            );

    //  顶点数据缓冲对象绑定
    VkBuffer vertexBuffers[] = {m_vertexBuffer};
    VkDeviceSize offsets[] = {0};
    vkCmdBindVertexBuffers(commandBuffer,
                           0,  //  将缓冲对象绑定到哪个号码开始输入绑定号, （估计是用于查找指定绑定号的绑定描述符VkVertexInputBindingDescription）
                           1,  //  总共要绑定多少个输入绑定号
                           vertexBuffers ,
                           offsets // 着色器读取每个vbo数据时的起始偏移
                           );
    vkCmdBindIndexBuffer(commandBuffer, m_indexBuffer,
                         0,  // 偏移， 字节
                         VK_INDEX_TYPE_UINT16);
    //  实际的绘画命令
//    vkCmdDraw(commandBuffer,      //  按顶点画图元
//              static_cast<uint32_t>(m_vertices.size()), //  顶点数量
//              1,    // 用于实例化渲染，如果不这样做，请使用 1
//              0,   // 用作顶点缓冲区的偏移量
//              0    // 用作实例渲染的偏移量
//              );

    vkCmdDrawIndexed(commandBuffer,         //  按索引画图元
                     static_cast<uint32_t>(m_indices.size()),  //  总共要画的索引数量
                     1,     //  要画的实例数量
                     0,     //  从第几个索引开始
                     0,     //  所有索引顶点值加上的值
                     0      //  实例偏移
                     );

    // 结束使用渲染通道
    vkCmdEndRenderPass(commandBuffer);

    //  一条渲染命令记录完毕
    if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
        throw std::runtime_error("failed to record command buffer!");
    }
}

void HelloTriangleApplication::createSyncObjects()
{
    m_imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
    m_renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
    m_inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);

    VkSemaphoreCreateInfo semaphoreInfo{};
    semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
    VkFenceCreateInfo fenceInfo{};
    fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
    fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; //  创建初始状态为已经有信号(已经触发)的栅栏

    for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
        if (vkCreateSemaphore(m_device, &semaphoreInfo, nullptr, &m_imageAvailableSemaphores[i]) != VK_SUCCESS ||
            vkCreateSemaphore(m_device, &semaphoreInfo, nullptr, &m_renderFinishedSemaphores[i]) != VK_SUCCESS ||
            vkCreateFence(m_device, &fenceInfo, nullptr, &m_inFlightFences[i]) != VK_SUCCESS) {
            throw std::runtime_error("failed to create semaphores!");
        }
    }

}

void HelloTriangleApplication::initVulkan()
{
    createInstance();
    setupDebugMessenger();
    createSurface();   // 一般应在创建vulkan实例后立即创建表面以免影响物理设备(显卡)的选择
    pickPhysicalDevice();
    createLogicalDevice();
    createSwapChain();
    createImageViews();
    createRenderPass();
    createDescriptorSetLayout();
    createGraphicsPipeline();
    createCommandPool();
    createDepthResources();
    createFramebuffers();
    createTextureImage();
    createTextureImageView();
    createTextureSampler();
    createVertexBuffer();
    createIndexBuffer();
    createUniformBuffers();
    createDescriptorPool();
    createDescriptorSets();
    createCommandBuffer();
    createSyncObjects();
}

void HelloTriangleApplication::updateUniformBuffer(uint32_t currentImage)
{
//    using hclock = std::chrono::high_resolution_clock;
//    hclock::now();
    static auto startTime = std::chrono::high_resolution_clock::now();
    auto currentTime = std::chrono::high_resolution_clock::now();
    float time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - startTime).count();

    UniformBufferObject ubo{};
    //  模型、视图、投影矩阵更新数据准备
    ubo.model = glm::rotate(glm::mat4(1.0f),
                            time * glm::radians(90.0f),
                            glm::vec3(0.0f, 0.0f, 1.0f)
                            );  //  旋转
    ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f),
                           glm::vec3(0.0f, 0.0f, 0.0f),
                           glm::vec3(0.0f, 0.0f, 1.0f));
    ubo.proj = glm::perspective(glm::radians(45.0f),
                                static_cast<float>(m_swapChainExtent.width) / static_cast<float>(m_swapChainExtent.height),
                                0.1f,
                                10.0f);

    ubo.proj[1][1] *= -1;  // 由于glm是为opengl设计的，所以它的Y方向与vulkan方向相反， 这里的Y变换m22要取反

    //  将新的模型、视图、投影矩阵数据传输到统一缓冲对象显存中
    void* data;
    vkMapMemory(m_device, m_uniformBuffersMemory[currentImage], 0, sizeof(ubo), 0, &data);
    memcpy(data, &ubo, sizeof(ubo));
    vkUnmapMemory(m_device, m_uniformBuffersMemory[currentImage]);

}

void HelloTriangleApplication::drawFrame()
{
    // 等待多个栅栏被触发
    // 这示例是cpu阻塞并等待上一帧gpu渲染和显示结束
    vkWaitForFences(m_device, 1, &m_inFlightFences[m_currentFrame],
                    VK_TRUE,  //  是否等待所有栅栏都有信号，还是其中一个
                    UINT64_MAX  //  等待超时时长, 单位为纳秒
                    );

    //  从交换链获取下一张要渲染的图像索引
    uint32_t imageIndex;
    VkResult result = vkAcquireNextImageKHR(m_device, m_swapChain,
                                            UINT64_MAX,  //  超时时长, 单位为纳秒
                                            m_imageAvailableSemaphores[m_currentFrame],  //  图像可用时(显示引擎使用完毕)要触发的信号量
                                            VK_NULL_HANDLE,   //  图像可用时(显示引擎使用完毕)要触发的栅栏
                                            &imageIndex   //  返回图像索引，无论下一帧图像是否被显示引擎使用完毕，它都会被返回
                                            );
    if (result == VK_ERROR_OUT_OF_DATE_KHR) {   // 返回交换链过期就进行交换链重新创建
        recreateSwapChain();
        return;
    } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
        throw std::runtime_error("failed to acquire swap chain image!");
    }

    //  栅栏有信号后，需要手动复位才能再次等待其信号。 因为return的原因，复位fence要放在判断是否要重建交换链之后。
    vkResetFences(m_device, 1, &m_inFlightFences[m_currentFrame]);

    //  复位命令缓冲，使记录或可执行状态命令缓冲复位为初始状态
    vkResetCommandBuffer(m_commandBuffers[m_currentFrame], 0);

    //  记录一条命令, 这里是渲染新获取到的图像
    recordCommandBuffer(m_commandBuffers[m_currentFrame], imageIndex);

    //  更新统一缓冲对象内容
    updateUniformBuffer(m_currentFrame);

    //  提交到队列信息
    VkSubmitInfo submitInfo{};
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    //waitStages 数组中的每个条目对应于 pWaitSemaphores 中具有相同索引的信号量。
    // 下面的代码应该是颜色附件被输出阶段要等待到图像被显示引擎防问完毕信号量
    VkSemaphore waitSemaphores[] = {m_imageAvailableSemaphores[m_currentFrame]};
    VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
    submitInfo.waitSemaphoreCount = 1;          //  等待的信号量
    submitInfo.pWaitSemaphores = waitSemaphores;
    submitInfo.pWaitDstStageMask = waitStages;  //  哪个管线阶段要等待信号量
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &m_commandBuffers[m_currentFrame];  //  发送哪个命令缓冲到队列中执行
    // 命令缓冲区被执行完毕时要产生的信号量
    VkSemaphore signalSemaphores[] = {m_renderFinishedSemaphores[m_currentFrame]};
    submitInfo.signalSemaphoreCount = 1;
    submitInfo.pSignalSemaphores = signalSemaphores;
    //  按发送信息内容，将命令发送到队列中，并在发送的命令执行完毕时触发信号量和栅栏
    if (vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, m_inFlightFences[m_currentFrame]) != VK_SUCCESS) {
        throw std::runtime_error("failed to submit draw command buffer!");
    }

    //  显示信息结构
    VkPresentInfoKHR presentInfo{};
    presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
    presentInfo.waitSemaphoreCount = 1;
    presentInfo.pWaitSemaphores = signalSemaphores;  //  需要等待的信号量
    //  需要显示的交换链和其本次显示的图像缓冲索引
    VkSwapchainKHR swapChains[] = {m_swapChain};
    presentInfo.swapchainCount = 1;
    presentInfo.pSwapchains = swapChains;
    presentInfo.pImageIndices = &imageIndex;
    //  所有要显示的交换链显示是否成功返回值指针
    presentInfo.pResults = nullptr; // Optional

    //  向显示队列压入一次显示操作
    result = vkQueuePresentKHR(m_presentQueue, &presentInfo);
    if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || m_framebufferResized) {
        m_framebufferResized = false;
         // 返回交换链过期或交换链不是最优化就进行交换链重新创建
        recreateSwapChain();
    } else if (result != VK_SUCCESS) {
        throw std::runtime_error("failed to present swap chain image!");
    }

    //  下一个渲染
    m_currentFrame = (m_currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
}

void HelloTriangleApplication::recreateSwapChain()
{
    {       // 窗体被最小化时， 要一直等待窗体恢复显示再进行交换链重建
        int width = 0, height = 0;
        glfwGetFramebufferSize(m_window, &width, &height);
        while (width == 0 || height == 0) {
            glfwGetFramebufferSize(m_window, &width, &height);
            glfwWaitEvents();
        }
    }

    vkDeviceWaitIdle(m_device);

    //  清理上一个swap chain及其相关的图像视图、帧缓冲
    {
        if(m_graphicsPipeline != VK_NULL_HANDLE){
            vkDestroyPipeline(m_device, m_graphicsPipeline, nullptr);
            m_graphicsPipeline = VK_NULL_HANDLE;
        }

        if(m_pipelineLayout != VK_NULL_HANDLE){
            vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr);
            m_pipelineLayout = VK_NULL_HANDLE;
        }

        if(m_renderPass != VK_NULL_HANDLE){
            vkDestroyRenderPass(m_device, m_renderPass, nullptr);
            m_renderPass = VK_NULL_HANDLE;
        }

        for (size_t i = 0; i < m_swapChainFramebuffers.size(); i++) {
            if(m_swapChainFramebuffers[i] != VK_NULL_HANDLE){
                vkDestroyFramebuffer(m_device, m_swapChainFramebuffers[i], nullptr);
                m_swapChainFramebuffers[i] = VK_NULL_HANDLE;
            }

        }

        for (size_t i = 0; i < m_swapChainImageViews.size(); i++) {
            if(m_swapChainImageViews[i] != VK_NULL_HANDLE){
                vkDestroyImageView(m_device, m_swapChainImageViews[i], nullptr);
                m_swapChainImageViews[i] = VK_NULL_HANDLE;
            }

        }

        if(m_swapChain != VK_NULL_HANDLE){
            vkDestroySwapchainKHR(m_device, m_swapChain, nullptr);
            m_swapChain = VK_NULL_HANDLE;
        }

        if(m_depthImageView != VK_NULL_HANDLE){
            vkDestroyImageView(m_device, m_depthImageView, nullptr);
            m_depthImageView = VK_NULL_HANDLE;
        }
        if(m_depthImage != VK_NULL_HANDLE){
            vkDestroyImage(m_device, m_depthImage, nullptr);
            m_depthImage = VK_NULL_HANDLE;
        }
        if(m_depthImageMemory != VK_NULL_HANDLE){
            vkFreeMemory(m_device, m_depthImageMemory, nullptr);
            m_depthImageMemory = VK_NULL_HANDLE;
        }

    }

    createSwapChain();
    createImageViews();
    createRenderPass();
    createGraphicsPipeline();
    createDepthResources();
    createFramebuffers();
}

void HelloTriangleApplication::mainLoop()
{
    if(!m_window) return;

    while (!glfwWindowShouldClose(m_window)) {      //  窗体事件循环
        glfwPollEvents();       //  处理等待的事件
        drawFrame();
    }

    //  等待逻辑设置所有操作结束， 保证可以释放与它相关的所有资源（例如信号量之类）
    //  实现类似功能的函数 vkQueueWaitIdle 等待某个队列没有操作
    vkDeviceWaitIdle(m_device);
}

void HelloTriangleApplication::cleanup()
{
    if(m_commandPool != VK_NULL_HANDLE) vkDestroyCommandPool(m_device, m_commandPool, nullptr);

    if(m_graphicsPipeline != VK_NULL_HANDLE) vkDestroyPipeline(m_device, m_graphicsPipeline, nullptr);

    if(m_pipelineLayout != VK_NULL_HANDLE) vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr);

    if(m_descriptorPool != VK_NULL_HANDLE) vkDestroyDescriptorPool(m_device, m_descriptorPool, nullptr);

    if(m_descriptorSetLayout != VK_NULL_HANDLE) vkDestroyDescriptorSetLayout(m_device, m_descriptorSetLayout, nullptr);

    if(m_renderPass != VK_NULL_HANDLE) vkDestroyRenderPass(m_device, m_renderPass, nullptr);

    for (auto framebuffer : m_swapChainFramebuffers) {
        vkDestroyFramebuffer(m_device, framebuffer, nullptr);
    }

    if(m_swapChain){
        for (auto imageView : m_swapChainImageViews) {   //  先销毁交换链缓冲图像对应的所有图像访问视口
            vkDestroyImageView(m_device, imageView, nullptr);
        }

        vkDestroySwapchainKHR(m_device, m_swapChain, nullptr);  // 销毁swap chain
    }
    if(m_depthImageView != VK_NULL_HANDLE) vkDestroyImageView(m_device, m_depthImageView, nullptr);
    if(m_depthImage != VK_NULL_HANDLE) vkDestroyImage(m_device, m_depthImage, nullptr);
    if(m_depthImageMemory != VK_NULL_HANDLE) vkFreeMemory(m_device, m_depthImageMemory, nullptr);

    if(m_textureSampler != VK_NULL_HANDLE) vkDestroySampler(m_device, m_textureSampler, nullptr);
    if(m_textureImageView != VK_NULL_HANDLE) vkDestroyImageView(m_device, m_textureImageView, nullptr);
    if(m_textureImage != VK_NULL_HANDLE) vkDestroyImage(m_device, m_textureImage, nullptr);
    if(m_textureImageMemory != VK_NULL_HANDLE) vkFreeMemory(m_device, m_textureImageMemory, nullptr);

    if(m_indexBuffer != VK_NULL_HANDLE) vkDestroyBuffer(m_device, m_indexBuffer, nullptr);
    if(m_indexBufferMemory != VK_NULL_HANDLE) vkFreeMemory(m_device, m_indexBufferMemory, nullptr);
    if(m_vertexBuffer != VK_NULL_HANDLE) vkDestroyBuffer(m_device, m_vertexBuffer, nullptr);
    if(m_vertexBufferMemory != VK_NULL_HANDLE) vkFreeMemory(m_device, m_vertexBufferMemory, nullptr);
    for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
        if(m_uniformBuffers[i] != VK_NULL_HANDLE) vkDestroyBuffer(m_device, m_uniformBuffers[i], nullptr);
        if(m_uniformBuffersMemory[i] != VK_NULL_HANDLE) vkFreeMemory(m_device, m_uniformBuffersMemory[i], nullptr);
    }


    for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
        if(m_renderFinishedSemaphores[i] != VK_NULL_HANDLE) vkDestroySemaphore(m_device, m_renderFinishedSemaphores[i], nullptr);
        if(m_imageAvailableSemaphores[i] != VK_NULL_HANDLE) vkDestroySemaphore(m_device, m_imageAvailableSemaphores[i], nullptr);
        if(m_inFlightFences[i] != VK_NULL_HANDLE) vkDestroyFence(m_device, m_inFlightFences[i], nullptr);
    }

    if(m_device != VK_NULL_HANDLE) vkDestroyDevice(m_device, nullptr);  // 销毁逻辑设备实例

    if(m_abs_surface != VK_NULL_HANDLE) vkDestroySurfaceKHR(m_instance, m_abs_surface, nullptr);    //  销毁渲染表面

    //////////////////////////////
    if (enableValidationLayers) {
        //  在vulkan实例中销毁调试消息对象
        auto func = reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>(
                    vkGetInstanceProcAddr(m_instance, "vkDestroyDebugUtilsMessengerEXT")
                    );
        if (func != nullptr) {
            func(m_instance, m_debugMessenger, nullptr);
        }
    }

    vkDestroyInstance(m_instance, nullptr);  // 销毁vulkan实例

    if(!m_window) glfwDestroyWindow(m_window);
    m_window = nullptr;

    glfwTerminate();  //  释放glfw库，与glfwInit对应
}
